package com.funny.stock.trade.service;

import com.funny.stock.base.redis.RedisTool;
import com.funny.stock.base.util.ResultUtil;
import com.funny.stock.base.vo.Result;
import com.funny.stock.base.vo.TradeType;
import com.funny.stock.data.collect.impl.RealTimeStockServiceDelegate;
import com.funny.stock.data.collect.impl.StockDataManager;
import com.funny.stock.data.vo.RealtimeStockData;
import com.funny.stock.data.vo.RedisKeyUtil;
import com.funny.stock.data.vo.SimpleRealtimeStockData;
import com.funny.stock.data.vo.StockConstants;
import com.funny.stock.data.vo.StockType;
import com.funny.stock.trade.dao.pojo.ComposeStockActionDO;
import com.funny.stock.trade.dao.pojo.ComposeStockItemDO;
import com.funny.stock.trade.dao.repo.ComposeStockItemRepo;
import com.funny.stock.trade.vo.EmulateComposeVO;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Description  
 * @Author  pengl
 * @Date 2020-05-17 
 */

@Service
public class ComposeStockItemService /*extends BaseCrudService<ComposeStockItemDO,Long>*/ {
	public static final Logger logger = LoggerFactory.getLogger(ComposeStockItemService.class);

	@Autowired
	ComposeStockItemRepo repository;

	@Autowired
	RedisTool redisTool;

	@Autowired
	StockDataManager stockDataManager;

	@Autowired
	TaskExecutor taskExecutor;

	@Autowired
	RealTimeStockServiceDelegate realTimeStockServiceDelegate;

	public boolean canSell(ComposeStockItemDO stockItem, Long tradeNumber){
		return stockItem != null && stockItem.getStockNumber().compareTo(tradeNumber) >= 0;
	}

	public Result<ComposeStockItemDO> doTradeStock(Long userInfoId, ComposeStockActionDO stockActionDO){
		ComposeStockItemDO stockItem = queryStockItem(userInfoId, stockActionDO.getComposeId(), stockActionDO.getStockCode());
		if(stockActionDO.getAction() == TradeType.SELL.getType() && !canSell(stockItem, stockActionDO.getActionNumber())){
			return ResultUtil.error("执行交易失败,卖出数量不足");
		}

		if(stockItem == null){
			stockItem = new ComposeStockItemDO();
			stockItem.setComposeId(stockActionDO.getComposeId());

			stockItem.setCreateId(userInfoId);
			stockItem.setCreateTime(new Date());

			stockItem.setStockCapital(stockActionDO.getTotalMoney());
			stockItem.setStockCode(stockActionDO.getStockCode());
			stockItem.setStockName(stockActionDO.getStockName());
			stockItem.setStockNumber(stockActionDO.getActionNumber());
			stockItem.setCostPrice(stockActionDO.getActionPrice());
			stockItem.setStockMarket(stockActionDO.getStockMarket());
		}else{
			stockItem.setUpdateId(userInfoId);
			stockItem.setUpdateTime(new Date());

			if(stockActionDO.getAction() == TradeType.BUY.getType()){
				long count = stockItem.getStockNumber() + stockActionDO.getActionNumber();
				stockItem.setStockNumber(count);
				BigDecimal totalCapital = stockActionDO.getTotalMoney().add(stockItem.getStockCapital());
				BigDecimal costPrice = totalCapital.divide(BigDecimal.valueOf(count),2,BigDecimal.ROUND_HALF_UP);
				stockItem.setStockCapital(totalCapital);
				stockItem.setCostPrice(costPrice);


			}else{
				long count = stockItem.getStockNumber() - stockActionDO.getActionNumber();
				if(count == 0){
					//全部卖出
					deleteStockItem(stockItem);
					return ResultUtil.success(null);
				}
				stockItem.setStockNumber(count);
				BigDecimal totalCapital = stockItem.getStockCapital().subtract(stockActionDO.getTotalMoney());
				BigDecimal costPrice = totalCapital.divide(BigDecimal.valueOf(count),2,BigDecimal.ROUND_HALF_UP);
				stockItem.setStockCapital(stockActionDO.getActionPrice().multiply(BigDecimal.valueOf(count)));
				stockItem.setCostPrice(costPrice);
			}
		}

		ComposeStockItemDO composeStockItemDO = saveStockItem(stockItem);
		logger.info("执行交易成功:{}",composeStockItemDO.getStockCode());
		return ResultUtil.success(composeStockItemDO);
	}

	public ComposeStockItemDO saveStockItem(ComposeStockItemDO stockItem){
		String stockItemKey = getStockItemKey(stockItem.getCreateId(), stockItem.getComposeId());
		ComposeStockItemDO save = repository.save(stockItem);
		redisTool.hPut(stockItemKey, stockItem.getStockCode(), save);
		redisTool.expire(stockItemKey, StockConstants.KEY_DEFAULT_EXPIRE_HOURS, TimeUnit.HOURS);
		return save;
	}

	public List<ComposeStockItemDO> queryStockItemList(Long createId,Long composeId){
		Map<String, ComposeStockItemDO> itemDOMap = queryCacheStockItemMap(createId, composeId);

		return new ArrayList<>(itemDOMap.values());
	}

	public List<SimpleRealtimeStockData> queryCacheStockDataList(List<String> codeList){
		//缓存中为空需要处理
		String stockKey = StockConstants.KEY_STOCK_REALTIME_SIMPLE_DATA_PREFIX ;
		List list = redisTool.hMultiGet(stockKey, codeList);
		return list;
	}

//	public void updateComposeAssetInfoWithDelay(EmulateComposeVO emulateComposeVO,long millis){
//		taskExecutor.execute(() -> {
//			try {
//				Thread.sleep(millis);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
//
//			updateComposeAssetInfo(emulateComposeVO);
//		});
//	}

	/**
	 * 实时计算资产信息
	 * 组合的全局资产信息，收益信息
	 * 总资产
	 * 可用资金
	 * 总市值
	 * 累计收益
	 * 当前仓位
	 * @param emulateComposeVO
	 */
	public void updateComposeAssetInfo(EmulateComposeVO emulateComposeVO){
		String composeAssetKey = getComposeAssetKey(emulateComposeVO.getCreateId(), emulateComposeVO.getId());

		List<ComposeStockItemDO> stockItemList = queryStockItemList(emulateComposeVO.getCreateId(), emulateComposeVO.getId());
		if(CollectionUtils.isEmpty(stockItemList)) return;

		BigDecimal sumCapital = BigDecimal.ZERO;
		for (ComposeStockItemDO item : stockItemList) {
			RealtimeStockData realtimeData = realTimeStockServiceDelegate.getRealtimeData(item.getStockCode(), StockType.getValueByType(item.getStockMarket()));
			if(realtimeData != null) {
				double price = realtimeData.getPrice();

				BigDecimal capital = BigDecimal.valueOf(price).multiply(BigDecimal.valueOf(item.getStockNumber()))
						.setScale(2, BigDecimal.ROUND_HALF_UP);
				item.setStockCapital(capital);
			}

			sumCapital = sumCapital.add(item.getStockCapital());
		}

		emulateComposeVO.setCapital(sumCapital);
		emulateComposeVO.setBalance(emulateComposeVO.getCash().add(sumCapital));
		BigDecimal positionPer = emulateComposeVO.getCapital().multiply(BigDecimal.valueOf(100)).divide(emulateComposeVO.getBalance(), 2, RoundingMode.HALF_UP);
		emulateComposeVO.setPositionPercent(positionPer);
		BigDecimal profit = emulateComposeVO.getBalance().subtract(emulateComposeVO.getInitBalance());
		emulateComposeVO.setProfitMoney(profit);
		BigDecimal profitPer = profit.multiply(BigDecimal.valueOf(100)).divide(emulateComposeVO.getInitBalance(), 2, RoundingMode.HALF_UP);
		emulateComposeVO.setProfitPercent(profitPer);

		redisTool.set(composeAssetKey,emulateComposeVO);
		redisTool.expire(composeAssetKey, StockConstants.KEY_DEFAULT_EXPIRE_HOURS, TimeUnit.HOURS);
	}

	public void fillComposeAssetInfo(EmulateComposeVO emulateComposeVO){
		if(emulateComposeVO.getBalance().equals(BigDecimal.ZERO)){
			emulateComposeVO.setBalance(emulateComposeVO.getInitBalance());
		}
		String composeAssetKey = getComposeAssetKey(emulateComposeVO.getCreateId(), emulateComposeVO.getId());
		EmulateComposeVO cacheVO = (EmulateComposeVO) redisTool.get(composeAssetKey);

		if(cacheVO == null){ // try again
			updateComposeAssetInfo(emulateComposeVO);

			cacheVO = (EmulateComposeVO) redisTool.get(composeAssetKey);
			if(cacheVO == null) return;
		}

		emulateComposeVO.setBalance(cacheVO.getBalance());
		emulateComposeVO.setCash(cacheVO.getCash());
		emulateComposeVO.setCapital(cacheVO.getCapital());
		emulateComposeVO.setProfitPercent(cacheVO.getProfitPercent());
		emulateComposeVO.setPositionPercent(cacheVO.getPositionPercent());
		emulateComposeVO.setProfitMoney(cacheVO.getProfitMoney());
	}

	public void removeComposeAssetCache(Long personId,Long composeId){
		String composeAssetKey = getComposeAssetKey(personId, composeId);
		redisTool.delete(composeAssetKey);
	}

	private Map<String, ComposeStockItemDO> queryCacheStockItemMap(Long createId,Long composeId){
		String stockItemKey = getStockItemKey(createId,composeId);
		Map<String, ComposeStockItemDO> entries = redisTool.hGetAll(stockItemKey);
		if(CollectionUtils.isEmpty(entries)){
			List<ComposeStockItemDO> list = repository.findByCreateIdAndComposeId(createId, composeId);
			Map dbMap = list.stream().collect(Collectors.toMap(ComposeStockItemDO::getStockCode, Function.identity(), (k1, k2) -> k2));
			redisTool.hPutAll(stockItemKey,dbMap);
			redisTool.expire(stockItemKey, StockConstants.KEY_DEFAULT_EXPIRE_HOURS, TimeUnit.HOURS);
			entries = dbMap;
		}

		return entries;
	}

	public ComposeStockItemDO queryStockItem(Long createId,Long composeId,String stockCode){
		String stockItemKey = getStockItemKey(createId, composeId);
		ComposeStockItemDO cacheItem = (ComposeStockItemDO) redisTool.hGet(stockItemKey, stockCode);
		if(cacheItem != null) return cacheItem;

		ComposeStockItemDO stockItem = repository.findByCreateIdAndStockCodeAndComposeId(createId, stockCode, composeId);
		redisTool.hPut(stockItemKey, stockCode, stockItem);
		redisTool.expire(stockItemKey, StockConstants.KEY_DEFAULT_EXPIRE_HOURS, TimeUnit.HOURS);
		return stockItem;
	}

	public void deleteStockItem(ComposeStockItemDO stockItemDO){
		repository.deleteById(stockItemDO.getId());
		String stockItemKey = getStockItemKey(stockItemDO.getCreateId(), stockItemDO.getComposeId());
		redisTool.hDelete(stockItemKey,stockItemDO.getStockCode());
	}

	private String getStockItemKey(Long createId,Long composeId){
		return RedisKeyUtil.getStockItemKey(createId, composeId);
	}

	private String getComposeAssetKey(Long createId,Long composeId){
		return RedisKeyUtil.getComposeAssetKey(createId, composeId);
	}


}
